using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using HslCommunication.BasicFramework;
using HslCommunication.Core;
using HslCommunication.Core.Address;
using HslCommunication.Core.Net;
using HslCommunication.Serial;

namespace HslCommunication.ModBus
{
	/// <inheritdoc cref="T:HslCommunication.ModBus.ModbusRtu" />
	public class ModbusRtuOverTcp : NetworkDeviceSoloBase
	{
		private byte station = 1;

		private bool isAddressStartWithZero = true;

		/// <inheritdoc cref="P:HslCommunication.ModBus.ModbusTcpNet.AddressStartWithZero" />
		public bool AddressStartWithZero
		{
			get
			{
				return isAddressStartWithZero;
			}
			set
			{
				isAddressStartWithZero = value;
			}
		}

		/// <inheritdoc cref="P:HslCommunication.ModBus.ModbusTcpNet.Station" />
		public byte Station
		{
			get
			{
				return station;
			}
			set
			{
				station = value;
			}
		}

		/// <inheritdoc cref="P:HslCommunication.ModBus.ModbusTcpNet.DataFormat" />
		public DataFormat DataFormat
		{
			get
			{
				return base.ByteTransform.DataFormat;
			}
			set
			{
				base.ByteTransform.DataFormat = value;
			}
		}

		/// <inheritdoc cref="P:HslCommunication.ModBus.ModbusTcpNet.IsStringReverse" />
		public bool IsStringReverse
		{
			get
			{
				return base.ByteTransform.IsStringReverseByteWord;
			}
			set
			{
				base.ByteTransform.IsStringReverseByteWord = value;
			}
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusRtu.#ctor" />
		public ModbusRtuOverTcp()
		{
			base.ByteTransform = new ReverseWordTransform();
			base.WordLength = 1;
			station = 1;
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.#ctor(System.String,System.Int32,System.Byte)" />
		public ModbusRtuOverTcp(string ipAddress, int port = 502, byte station = 1)
		{
			base.ByteTransform = new ReverseWordTransform();
			IpAddress = ipAddress;
			Port = port;
			base.WordLength = 1;
			this.station = station;
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusRtu.CheckModbusTcpResponse(System.Byte[])" />
		protected virtual OperateResult<byte[]> CheckModbusTcpResponse(byte[] send)
		{
			send = ModbusInfo.PackCommandToRtu(send);
			OperateResult<byte[]> operateResult = ReadFromCoreServer(send);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			if (operateResult.Content.Length < 5)
			{
				return new OperateResult<byte[]>(StringResources.Language.ReceiveDataLengthTooShort + "5");
			}
			if (!SoftCRC16.CheckCRC16(operateResult.Content))
			{
				return new OperateResult<byte[]>(StringResources.Language.ModbusCRCCheckFailed + SoftBasic.ByteToHexString(operateResult.Content, ' '));
			}
			if (send[1] + 128 == operateResult.Content[1])
			{
				return new OperateResult<byte[]>(operateResult.Content[2], ModbusInfo.GetDescriptionByErrorCode(operateResult.Content[2]));
			}
			if (send[1] != operateResult.Content[1])
			{
				return new OperateResult<byte[]>(operateResult.Content[1], "Receive Command Check Failed: ");
			}
			return ModbusInfo.ExtractActualData(ModbusInfo.ExplodeRtuCommandToCore(operateResult.Content));
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusRtuOverTcp.CheckModbusTcpResponse(System.Byte[])" />
		protected virtual async Task<OperateResult<byte[]>> CheckModbusTcpResponseAsync(byte[] send)
		{
			send = ModbusInfo.PackCommandToRtu(send);
			OperateResult<byte[]> result = await ReadFromCoreServerAsync(send);
			if (!result.IsSuccess)
			{
				return result;
			}
			if (result.Content.Length < 5)
			{
				return new OperateResult<byte[]>(StringResources.Language.ReceiveDataLengthTooShort + "5");
			}
			if (!SoftCRC16.CheckCRC16(result.Content))
			{
				return new OperateResult<byte[]>(StringResources.Language.ModbusCRCCheckFailed + SoftBasic.ByteToHexString(result.Content, ' '));
			}
			if (send[1] + 128 == result.Content[1])
			{
				return new OperateResult<byte[]>(result.Content[2], ModbusInfo.GetDescriptionByErrorCode(result.Content[2]));
			}
			if (send[1] != result.Content[1])
			{
				return new OperateResult<byte[]>(result.Content[1], "Receive Command Check Failed: ");
			}
			return ModbusInfo.ExtractActualData(ModbusInfo.ExplodeRtuCommandToCore(result.Content));
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.ReadModBus(HslCommunication.Core.Address.ModbusAddress,System.UInt16)" />
		private OperateResult<byte[]> ReadModBus(ModbusAddress address, ushort length)
		{
			OperateResult<byte[]> operateResult = ModbusInfo.BuildReadModbusCommand(address, length);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			return CheckModbusTcpResponse(operateResult.Content);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.ReadCoil(System.String)" />
		public OperateResult<bool> ReadCoil(string address)
		{
			return ReadBool(address);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.ReadCoil(System.String,System.UInt16)" />
		public OperateResult<bool[]> ReadCoil(string address, ushort length)
		{
			return ReadBool(address, length);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.ReadDiscrete(System.String)" />
		public OperateResult<bool> ReadDiscrete(string address)
		{
			return ByteTransformHelper.GetResultFromArray(ReadDiscrete(address, 1));
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.ReadDiscrete(System.String,System.UInt16)" />
		public OperateResult<bool[]> ReadDiscrete(string address, ushort length)
		{
			OperateResult<byte[]> operateResult = ModbusInfo.BuildReadModbusCommand(address, length, Station, AddressStartWithZero, 2);
			if (!operateResult.IsSuccess)
			{
				return OperateResult.CreateFailedResult<bool[]>(operateResult);
			}
			OperateResult<byte[]> operateResult2 = CheckModbusTcpResponse(operateResult.Content);
			if (!operateResult2.IsSuccess)
			{
				return OperateResult.CreateFailedResult<bool[]>(operateResult2);
			}
			return OperateResult.CreateSuccessResult(SoftBasic.ByteToBoolArray(operateResult2.Content, length));
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.Read(System.String,System.UInt16)" />
		public override OperateResult<byte[]> Read(string address, ushort length)
		{
			OperateResult<ModbusAddress> operateResult = ModbusInfo.AnalysisAddress(address, Station, isAddressStartWithZero, 3);
			if (!operateResult.IsSuccess)
			{
				return OperateResult.CreateFailedResult<byte[]>(operateResult);
			}
			List<byte> list = new List<byte>();
			ushort num = 0;
			while (num < length)
			{
				ushort num2 = (ushort)Math.Min(length - num, 120);
				OperateResult<byte[]> operateResult2 = ReadModBus(operateResult.Content.AddressAdd(num), num2);
				if (!operateResult2.IsSuccess)
				{
					return OperateResult.CreateFailedResult<byte[]>(operateResult2);
				}
				list.AddRange(operateResult2.Content);
				num = (ushort)(num + num2);
			}
			return OperateResult.CreateSuccessResult(list.ToArray());
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.Write(System.String,System.Byte[])" />
		public override OperateResult Write(string address, byte[] value)
		{
			OperateResult<byte[]> operateResult = ModbusInfo.BuildWriteWordModbusCommand(address, value, Station, AddressStartWithZero, 16);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			return CheckModbusTcpResponse(operateResult.Content);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.WriteOneRegister(System.String,System.Int16)" />
		public OperateResult WriteOneRegister(string address, short value)
		{
			OperateResult<byte[]> operateResult = ModbusInfo.BuildWriteWordModbusCommand(address, value, Station, AddressStartWithZero, 6);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			return CheckModbusTcpResponse(operateResult.Content);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.WriteOneRegister(System.String,System.UInt16)" />
		public OperateResult WriteOneRegister(string address, ushort value)
		{
			OperateResult<byte[]> operateResult = ModbusInfo.BuildWriteWordModbusCommand(address, value, Station, AddressStartWithZero, 6);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			return CheckModbusTcpResponse(operateResult.Content);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.WriteMask(System.String,System.UInt16,System.UInt16)" />
		public OperateResult WriteMask(string address, ushort andMask, ushort orMask)
		{
			OperateResult<byte[]> operateResult = ModbusInfo.BuildWriteMaskModbusCommand(address, andMask, orMask, Station, AddressStartWithZero, 22);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			return CheckModbusTcpResponse(operateResult.Content);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusRtuOverTcp.ReadModBus(HslCommunication.Core.Address.ModbusAddress,System.UInt16)" />
		private async Task<OperateResult<byte[]>> ReadModBusAsync(ModbusAddress address, ushort length)
		{
			OperateResult<byte[]> command = ModbusInfo.BuildReadModbusCommand(address, length);
			if (!command.IsSuccess)
			{
				return command;
			}
			return await CheckModbusTcpResponseAsync(command.Content);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.ReadCoilAsync(System.String)" />
		public async Task<OperateResult<bool>> ReadCoilAsync(string address)
		{
			return await ReadBoolAsync(address);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.ReadCoilAsync(System.String,System.UInt16)" />
		public async Task<OperateResult<bool[]>> ReadCoilAsync(string address, ushort length)
		{
			return await ReadBoolAsync(address, length);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.ReadDiscreteAsync(System.String)" />
		public async Task<OperateResult<bool>> ReadDiscreteAsync(string address)
		{
			return ByteTransformHelper.GetResultFromArray(await ReadDiscreteAsync(address, 1));
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.ReadDiscreteAsync(System.String,System.UInt16)" />
		public async Task<OperateResult<bool[]>> ReadDiscreteAsync(string address, ushort length)
		{
			OperateResult<byte[]> command = ModbusInfo.BuildReadModbusCommand(address, length, Station, AddressStartWithZero, 2);
			if (!command.IsSuccess)
			{
				return OperateResult.CreateFailedResult<bool[]>(command);
			}
			OperateResult<byte[]> read = await CheckModbusTcpResponseAsync(command.Content);
			if (!read.IsSuccess)
			{
				return OperateResult.CreateFailedResult<bool[]>(read);
			}
			return OperateResult.CreateSuccessResult(SoftBasic.ByteToBoolArray(read.Content, length));
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.ReadAsync(System.String,System.UInt16)" />
		public override async Task<OperateResult<byte[]>> ReadAsync(string address, ushort length)
		{
			OperateResult<ModbusAddress> analysis = ModbusInfo.AnalysisAddress(address, Station, isAddressStartWithZero, 3);
			if (!analysis.IsSuccess)
			{
				return OperateResult.CreateFailedResult<byte[]>(analysis);
			}
			List<byte> lists = new List<byte>();
			ushort alreadyFinished = 0;
			while (alreadyFinished < length)
			{
				ushort lengthTmp = (ushort)Math.Min(length - alreadyFinished, 120);
				OperateResult<byte[]> read = await ReadModBusAsync(analysis.Content.AddressAdd(alreadyFinished), lengthTmp);
				if (!read.IsSuccess)
				{
					return OperateResult.CreateFailedResult<byte[]>(read);
				}
				lists.AddRange(read.Content);
				alreadyFinished = (ushort)(alreadyFinished + lengthTmp);
			}
			return OperateResult.CreateSuccessResult(lists.ToArray());
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.WriteAsync(System.String,System.Byte[])" />
		public override async Task<OperateResult> WriteAsync(string address, byte[] value)
		{
			OperateResult<byte[]> command = ModbusInfo.BuildWriteWordModbusCommand(address, value, Station, AddressStartWithZero, 16);
			if (!command.IsSuccess)
			{
				return command;
			}
			return await CheckModbusTcpResponseAsync(command.Content);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.WriteOneRegisterAsync(System.String,System.Int16)" />
		public async Task<OperateResult> WriteOneRegisterAsync(string address, short value)
		{
			OperateResult<byte[]> command = ModbusInfo.BuildWriteWordModbusCommand(address, value, Station, AddressStartWithZero, 6);
			if (!command.IsSuccess)
			{
				return command;
			}
			return await CheckModbusTcpResponseAsync(command.Content);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.WriteOneRegisterAsync(System.String,System.UInt16)" />
		public async Task<OperateResult> WriteOneRegisterAsync(string address, ushort value)
		{
			OperateResult<byte[]> command = ModbusInfo.BuildWriteWordModbusCommand(address, value, Station, AddressStartWithZero, 6);
			if (!command.IsSuccess)
			{
				return command;
			}
			return await CheckModbusTcpResponseAsync(command.Content);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusRtuOverTcp.WriteMask(System.String,System.UInt16,System.UInt16)" />
		public async Task<OperateResult> WriteMaskAsync(string address, ushort andMask, ushort orMask)
		{
			return await Task.Run(() => WriteMask(address, andMask, orMask));
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.ReadBool(System.String,System.UInt16)" />
		public override OperateResult<bool[]> ReadBool(string address, ushort length)
		{
			OperateResult<byte[]> operateResult = ModbusInfo.BuildReadModbusCommand(address, length, Station, AddressStartWithZero, 1);
			if (!operateResult.IsSuccess)
			{
				return OperateResult.CreateFailedResult<bool[]>(operateResult);
			}
			OperateResult<byte[]> operateResult2 = CheckModbusTcpResponse(operateResult.Content);
			if (!operateResult2.IsSuccess)
			{
				return OperateResult.CreateFailedResult<bool[]>(operateResult2);
			}
			return OperateResult.CreateSuccessResult(SoftBasic.ByteToBoolArray(operateResult2.Content, length));
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.Write(System.String,System.Boolean[])" />
		public override OperateResult Write(string address, bool[] values)
		{
			OperateResult<byte[]> operateResult = ModbusInfo.BuildWriteBoolModbusCommand(address, values, Station, AddressStartWithZero, 15);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			return CheckModbusTcpResponse(operateResult.Content);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.Write(System.String,System.Boolean)" />
		public override OperateResult Write(string address, bool value)
		{
			OperateResult<byte[]> operateResult = ModbusInfo.BuildWriteBoolModbusCommand(address, value, Station, AddressStartWithZero, 5);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			return CheckModbusTcpResponse(operateResult.Content);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.ReadBoolAsync(System.String,System.UInt16)" />
		public override async Task<OperateResult<bool[]>> ReadBoolAsync(string address, ushort length)
		{
			OperateResult<byte[]> command = ModbusInfo.BuildReadModbusCommand(address, length, Station, AddressStartWithZero, 1);
			if (!command.IsSuccess)
			{
				return OperateResult.CreateFailedResult<bool[]>(command);
			}
			OperateResult<byte[]> read = await CheckModbusTcpResponseAsync(command.Content);
			if (!read.IsSuccess)
			{
				return OperateResult.CreateFailedResult<bool[]>(read);
			}
			return OperateResult.CreateSuccessResult(SoftBasic.ByteToBoolArray(read.Content, length));
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.WriteAsync(System.String,System.Boolean[])" />
		public override async Task<OperateResult> WriteAsync(string address, bool[] values)
		{
			OperateResult<byte[]> command = ModbusInfo.BuildWriteBoolModbusCommand(address, values, Station, AddressStartWithZero, 15);
			if (!command.IsSuccess)
			{
				return command;
			}
			return await CheckModbusTcpResponseAsync(command.Content);
		}

		/// <inheritdoc cref="M:HslCommunication.ModBus.ModbusTcpNet.WriteAsync(System.String,System.Boolean)" />
		public override async Task<OperateResult> WriteAsync(string address, bool value)
		{
			OperateResult<byte[]> command = ModbusInfo.BuildWriteBoolModbusCommand(address, value, Station, AddressStartWithZero, 5);
			if (!command.IsSuccess)
			{
				return command;
			}
			return await CheckModbusTcpResponseAsync(command.Content);
		}

		/// <inheritdoc />
		public override string ToString()
		{
			return $"ModbusRtuOverTcp[{IpAddress}:{Port}]";
		}
	}
}
